]> dgit.raspbian.org Git - gnome-shell.git/commitdiff
main: Prepend RPATH or RUNPATH paths to gir search paths
authorJonas Ådahl <jadahl@gmail.com>
Wed, 17 Aug 2022 19:43:21 +0000 (21:43 +0200)
committerJonas Ådahl <jadahl@gmail.com>
Fri, 25 Nov 2022 21:09:37 +0000 (22:09 +0100)
If one wants to run tests the non-installed gnome-shell, that currently
fails as gnome-shell the executable attempts to link against
./build/src/libgnome-shell.so, but when GObject introspection tries to
find what library to link to for Shell, it goes to the installed
libgnome-shell.so, causing two different versions of libgnome-shell.so
to be loaded.

This, however, can be avoided thanks to meson adding $ORIGIN paths to
relevant libraries before installing an executable. What this means in
practice is that we can inspect ourself upon startup, discover whether
the RPATH/RUNPATH header contains $ORIGIN, and if so, expand it to the
directory containing the executable, and prepend the introspection
search paths with said directory.

This effectively means that the introspection machinery now finds the
same library that the linker linked the gnome-shell executable with,
making it run successfully.

It's not possible to use $GI_TYPELIB_PATH since
g_irepository_prepend_library_path() takes precedence. There is no
"append" variant of that API.

Part-of: <https://gitlab.gnome.org/GNOME/gnome-shell/-/merge_requests/1349>

config.h.meson
meson.build
src/main.c

index ff355d306215be6d0e158e177bd184c03990a03f..928a758ae27c6320091c73e3c91f51ff859ec1d5 100644 (file)
@@ -36,3 +36,6 @@
 
 /* Define if polkit defines autocleanup functions */
 #mesondefine HAVE_POLKIT_AUTOCLEANUP
+
+/* Define to 1 if the <elf.h> and <link.h> header file exist. */
+#mesondefine HAVE_EXE_INTROSPECTION
index 252fb1ec4e607abeb31b2fdd78a897aea73fba43..0be7bf7695d55b9ad89fa7087eed1aea829aceb4 100644 (file)
@@ -148,6 +148,8 @@ cdata.set('HAVE_FDWALK', cc.has_function('fdwalk'))
 cdata.set('HAVE_MALLINFO', cc.has_function('mallinfo'))
 cdata.set('HAVE_MALLINFO2', cc.has_function('mallinfo2'))
 cdata.set('HAVE_SYS_RESOURCE_H', cc.has_header('sys/resource.h'))
+cdata.set('HAVE_EXE_INTROSPECTION',
+  cc.has_header('elf.h') and cc.has_header('link.h'))
 cdata.set('HAVE__NL_TIME_FIRST_WEEKDAY',
   cc.has_header_symbol('langinfo.h', '_NL_TIME_FIRST_WEEKDAY')
 )
index 269fe56c921bcb4ead2e31ea4825e51dd958b790..9913bbccbd9cb8032df9fe53bcd1f777938b440f 100644 (file)
 #include <meta/meta-plugin.h>
 #include <meta/prefs.h>
 #include <atk-bridge.h>
+#include <link.h>
+
+#ifdef HAVE_EXE_INTROSPECTION
+#include <elf.h>
+#endif
 
 #include "shell-global.h"
 #include "shell-global-private.h"
@@ -123,6 +128,74 @@ shell_dbus_init (gboolean replace)
   g_object_unref (session);
 }
 
+#ifdef HAVE_EXE_INTROSPECTION
+static void
+maybe_add_rpath_introspection_paths (void)
+{
+  ElfW (Dyn) *dyn;
+  ElfW (Dyn) *rpath = NULL;
+  ElfW (Dyn) *runpath = NULL;
+  const char *strtab = NULL;
+  g_auto (GStrv) paths = NULL;
+  g_autofree char *exe_dir = NULL;
+  GStrv str;
+
+  for (dyn = _DYNAMIC; dyn->d_tag != DT_NULL; dyn++)
+    {
+      if (dyn->d_tag == DT_RPATH)
+        rpath = dyn;
+      else if (dyn->d_tag == DT_RUNPATH)
+        runpath = dyn;
+      else if (dyn->d_tag == DT_STRTAB)
+        strtab = (const char *) dyn->d_un.d_val;
+    }
+
+  if ((!rpath && !runpath) || !strtab)
+    return;
+
+  if (rpath)
+    paths = g_strsplit (strtab + rpath->d_un.d_val, ":", -1);
+  else
+    paths = g_strsplit (strtab + runpath->d_un.d_val, ":", -1);
+
+  if (!paths)
+    return;
+
+  for (str = paths; *str; str++)
+    {
+      g_autoptr (GError) error = NULL;
+      g_autoptr (GString) rpath_dir = NULL;
+
+      if (!strstr (*str, "$ORIGIN"))
+        continue;
+
+      if (!exe_dir)
+        {
+          g_autofree char *exe_path = NULL;
+
+          exe_path = g_file_read_link ("/proc/self/exe", &error);
+          if (!exe_path)
+            {
+              g_warning ("Failed to find directory of executable: %s",
+                         error->message);
+              return;
+            }
+
+          exe_dir = g_path_get_dirname (exe_path);
+        }
+
+      rpath_dir = g_string_new (*str);
+      g_string_replace (rpath_dir, "$ORIGIN", exe_dir, 0);
+
+      g_debug ("Prepending RPATH directory '%s' "
+               "to introsepciton library search path",
+               rpath_dir->str);
+      g_irepository_prepend_search_path (rpath_dir->str);
+      g_irepository_prepend_library_path (rpath_dir->str);
+    }
+}
+#endif /* HAVE_EXE_INTROSPECTION */
+
 static void
 shell_introspection_init (void)
 {
@@ -137,6 +210,10 @@ shell_introspection_init (void)
    */
   g_irepository_prepend_library_path (MUTTER_TYPELIB_DIR);
   g_irepository_prepend_library_path (GNOME_SHELL_PKGLIBDIR);
+
+#ifdef HAVE_EXE_INTROSPECTION
+  maybe_add_rpath_introspection_paths ();
+#endif
 }
 
 static void